package org.nice.in.json;



import java.util.Arrays;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Represents a "nice" path through a JSONObject into an element of interest. 
 * @author katie
 */
public class NiceJSONPath {

    String[] path;

    /**
     * Convenience - equivalent to NiceJSONPath(null,null)
     */
    public NiceJSONPath() {
        this(null, null);
    }

    /**
     * Convenience - equivalent to NiceJSONPath(directPath,null).
     * For example, given a JSONObject, the path "animal>cat>domestic"
     * will indicate that this object has another JSONObject associated with "animal," and that, in turn,
     * has a JSONObject associated with "cat." Once we're there, we can retrieve whatever this third
     * JSONObject has associated with "domestic." This can be accomplished
     * using the getObject(Object) method, or either of the
     * convenience methods getObject(String) and getObjectFromArray(JSONArray,int)
     * @param directPath 
     */
    public NiceJSONPath(String directPath) {
        this(directPath, null);
    }

    /**
     * Create a long, multi-tiered path. For example, given a JSONObject, the path "animal>cat>domestic"
     * will indicate that this object has another JSONObject associated with "animal," and that, in turn,
     * has a JSONObject associated with "cat." Once we're there, we can retrieve whatever this third
     * JSONObject has associated with "domestic."
     * @param longPath can be null or empty if top-level object is of interest
     * @param delim can be null or empty is the object of interest is just one level down
     */
    public NiceJSONPath(String longPath, String delim) {
        if (longPath == null || longPath.isEmpty()) {
            path = new String[0];
        } else if (delim == null || delim.isEmpty()) {
            path = new String[]{longPath};
        } else {
            path = longPath.split(delim);
        }
    }

    /**
     * Allows passing in a source feed rather than the
     * JSONObject constructed from it; it constructs this object and then calls getObject() on it.
     * This exists for convenience - should exceptions occur, this method
     * will catch them and print to standard error what happened.
     * @param feed
     * @return 
     */
    protected Object getObject(String feed) {
        try {
            return getObject(new JSONObject(feed));
        } catch (JSONException je) {
            System.err.println("NiceJSONPath(" + Arrays.toString(path) + ").getObject(<feed>) failed: " + je.getMessage());
            return null;
        }
    }
    /**
     * Uses the constructed path to retrieve an arbitrary object. For example, given a JSONObject, the path "animal>cat>domestic"
     * will indicate that this object has another JSONObject associated with "animal," and that, in turn,
     * has a JSONObject associated with "cat." Once we're there, we can retrieve whatever this third
     * JSONObject has associated with "domestic." This is returned as an Object and can be cast appropriately.
     * As long as there are steps left in the path, the result will be cast into JSONObject; if this is not an okay thing
     * to be doing, failure will be imminent.
     * @param jo this should be a JSONObject on which the given path exists
     * @return 
     */
    protected Object getObject(Object jo) {
        try {
            for (int i = 0; i < path.length; i++) {
                jo = ((JSONObject)jo).get(path[i]);
            }
            return jo;
        } catch (JSONException je) {
            System.err.println("NiceJSONPath(" + Arrays.toString(path) + ").getObject(<object>) failed: " + je.getMessage());
            return null;
        }
    }
    /**
     * Retrieves an object from a JSONArray at the specified index by first
     * getting the object  at that instance and then calling getObject() on it.
     * This is done for convenience - should exceptions occur, this method
     * will catch them and print to standard error what happened.
     * @param ja
     * @param index
     * @return 
     */
    protected Object getObjectFromArray(JSONArray ja, int index) {
        try {
            return getObject(ja.getJSONObject(index));
        } catch (JSONException je) {
            System.err.println("NiceJSONPath(" + Arrays.toString(path) + ").getObjectFromArray(<JSONarray>,"+index+") failed: " + je.getMessage());
            return null;
        }
    }
}
